diff options
| author | Factiven <[email protected]> | 2023-12-24 13:03:54 +0700 |
|---|---|---|
| committer | Factiven <[email protected]> | 2023-12-24 13:03:54 +0700 |
| commit | 50a0f0240d7fef133eb5acc1bea2b1168b08e9db (patch) | |
| tree | 307e09e505580415a58d64b5fc3580e9235869f1 /pages/en/search/[...param].js | |
| parent | Update README.md (#104) (diff) | |
| download | moopa-50a0f0240d7fef133eb5acc1bea2b1168b08e9db.tar.xz moopa-50a0f0240d7fef133eb5acc1bea2b1168b08e9db.zip | |
migrate to typescript
Diffstat (limited to 'pages/en/search/[...param].js')
| -rw-r--r-- | pages/en/search/[...param].js | 571 |
1 files changed, 0 insertions, 571 deletions
diff --git a/pages/en/search/[...param].js b/pages/en/search/[...param].js deleted file mode 100644 index c1fd94c..0000000 --- a/pages/en/search/[...param].js +++ /dev/null @@ -1,571 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import { motion as m } from "framer-motion"; -import Skeleton from "react-loading-skeleton"; -import { useRouter } from "next/router"; -import Link from "next/link"; -import Head from "next/head"; -import Footer from "@/components/shared/footer"; - -import Image from "next/image"; -import { aniAdvanceSearch } from "@/lib/anilist/aniAdvanceSearch"; -import MultiSelector from "@/components/search/dropdown/multiSelector"; -import SingleSelector from "@/components/search/dropdown/singleSelector"; -import { - animeFormatOptions, - formatOptions, - genreOptions, - mangaFormatOptions, - mediaType, - seasonOptions, - tagsOption, - yearOptions, -} from "@/components/search/selection"; -import InputSelect from "@/components/search/dropdown/inputSelect"; -import { Cog6ToothIcon, TrashIcon } from "@heroicons/react/20/solid"; -import useDebounce from "@/lib/hooks/useDebounce"; -import { NewNavbar } from "@/components/shared/NavBar"; -import MobileNav from "@/components/shared/MobileNav"; -import SearchByImage from "@/components/search/searchByImage"; -import { PlayIcon } from "@heroicons/react/24/outline"; - -export async function getServerSideProps(context) { - const { param } = context.query; - - const { search, format, genres, season, year } = context.query; - - let getFormat, getSeason, getYear; - let getGenres = []; - - if (genres) { - const gr = genreOptions.find( - (i) => i.value.toLowerCase() === genres.toLowerCase() - ); - getGenres.push(gr); - } - - if (season) { - getSeason = seasonOptions.find( - (i) => i.value.toLowerCase() === season.toLowerCase() - ); - if (!year) { - const now = new Date().getFullYear(); - getYear = yearOptions.find((i) => i.value === now.toString()); - } else { - getYear = yearOptions.find((i) => i.value === year); - } - } - - if (format) { - getFormat = formatOptions.find( - (i) => i.value.toLowerCase() === format.toLowerCase() - ); - } - - if (!param && param.length !== 1) { - return { - notFound: true, - }; - } - - const typeIndex = param[0] === "anime" ? 0 : 1; - - return { - props: { - index: typeIndex, - query: search || null, - formats: getFormat || null, - seasons: getSeason || null, - years: getYear || null, - genres: getGenres || null, - }, - }; -} - -export default function Card({ - index, - query, - genres, - formats, - seasons, - years, -}) { - const inputRef = useRef(null); - const router = useRouter(); - - const [data, setData] = useState(); - const [imageSearch, setImageSearch] = useState(); - - const [loading, setLoading] = useState(true); - - const [search, setQuery] = useState(query); - const debounceSearch = useDebounce(search, 500); - - const [type, setSelectedType] = useState(mediaType[index]); - const [year, setYear] = useState(years); - const [season, setSeason] = useState(seasons); - const [sort, setSelectedSort] = useState(); - const [genre, setGenre] = useState(genres); - const [format, setFormat] = useState(formats); - - const [isVisible, setIsVisible] = useState(false); - - const [page, setPage] = useState(1); - const [nextPage, setNextPage] = useState(true); - - async function advance() { - setLoading(true); - const data = await aniAdvanceSearch({ - search: debounceSearch, - type: type?.value, - genres: genre, - page: page, - sort: sort?.value, - format: format?.value, - season: season?.value, - seasonYear: year?.value, - }); - if (data?.media?.length === 0) { - setNextPage(false); - setLoading(false); - } else if (data !== null && page > 1) { - setData((prevData) => { - return [...(prevData ?? []), ...data?.media]; - }); - setNextPage(data?.pageInfo.hasNextPage); - setLoading(false); - } else { - setData(data?.media); - setNextPage(data?.pageInfo.hasNextPage); - setLoading(false); - } - } - - useEffect(() => { - setData(null); - setPage(1); - setNextPage(true); - advance(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - debounceSearch, - type?.value, - sort?.value, - genre, - format?.value, - season?.value, - year?.value, - ]); - - useEffect(() => { - if (imageSearch) return; - advance(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [page, imageSearch]); - - useEffect(() => { - function handleScroll() { - if (imageSearch) { - window.removeEventListener("scroll", handleScroll); - return; - } - if (page > 10 || !nextPage) { - window.removeEventListener("scroll", handleScroll); - return; - } - - if ( - window.innerHeight + window.pageYOffset >= - document.body.offsetHeight - 3 - ) { - setPage((prevPage) => prevPage + 1); - } - } - - window.addEventListener("scroll", handleScroll); - - return () => window.removeEventListener("scroll", handleScroll); - }, [page, nextPage, imageSearch]); - - const handleKeyDown = async (event) => { - if (event.key === "Enter") { - event.preventDefault(); - const inputValue = event.target.value; - if (inputValue === "") { - setQuery(null); - } else { - setQuery(inputValue); - } - } - }; - - function trash() { - setImageSearch(); - setQuery(); - setGenre(); - setFormat(); - setSelectedSort(); - setSeason(); - setYear(); - router.push(`/en/search/${mediaType[index]?.value?.toLowerCase()}`); - } - - function handleVisible() { - setIsVisible(!isVisible); - } - - const handleVideoHover = (hovered, id) => { - const updatedImageSearch = imageSearch?.map((item) => { - if (item.filename === id) { - return { ...item, hovered }; - } - return item; - }); - setImageSearch(updatedImageSearch); - }; - - // console.log({ loading, data }); - - return ( - <> - <Head> - <title>Moopa - search</title> - <meta name="title" content="Search" /> - <meta name="description" content="Search your favourites Anime/Manga" /> - <link rel="icon" href="/svg/c.svg" /> - </Head> - - <NewNavbar - scrollP={10} - withNav={true} - shrink={true} - paddingY="py-1 lg:py-3" - /> - <MobileNav hideProfile={true} /> - <main className="w-screen min-h-screen z-40 py-14 lg:py-24"> - <div className="max-w-screen-xl flex flex-col gap-3 mx-auto"> - <div className="w-full flex justify-between items-end gap-2 my-3 lg:gap-10 px-5 xl:px-0 relative"> - <div className="hidden lg:flex items-end w-full gap-5 z-50"> - <InputSelect - inputRef={inputRef} - data={mediaType} - label="Search" - keyDown={handleKeyDown} - query={search} - setQuery={setQuery} - selected={type} - setSelected={setSelectedType} - /> - {/* GENRES */} - <MultiSelector - data={genreOptions} - other={tagsOption} - selected={genre} - setSelected={setGenre} - label="Genres" - inputRef={inputRef} - /> - {/* SORT */} - {/* <SingleSelector - data={sortOptions} - selected={sort} - setSelected={setSelectedSort} - label="Sort" - /> */} - {/* FORMAT */} - <SingleSelector - data={index === 0 ? animeFormatOptions : mangaFormatOptions} - selected={format} - setSelected={setFormat} - label="Format" - /> - {/* SEASON */} - <SingleSelector - data={seasonOptions} - selected={season} - setSelected={setSeason} - label="Season" - /> - {/* YEAR */} - <SingleSelector - data={yearOptions} - selected={year} - setSelected={setYear} - label="Year" - /> - </div> - <div className="w-full lg:hidden"> - <InputSelect - inputRef={inputRef} - data={mediaType} - label="Search" - keyDown={handleKeyDown} - query={search} - setQuery={setQuery} - selected={type} - setSelected={setSelectedType} - /> - </div> - - <div className="flex gap-2"> - <div - className="lg:hidden py-2 px-2 bg-secondary rounded flex justify-center items-center cursor-pointer hover:bg-opacity-75 transition-all duration-100 group" - onClick={handleVisible} - > - <Cog6ToothIcon className="w-5 h-5" /> - </div> - <SearchByImage setMedia={setData} setData={setImageSearch} /> - <div - className="py-2 px-2 bg-secondary rounded flex justify-center items-center cursor-pointer hover:bg-opacity-75 transition-all duration-100 group" - onClick={trash} - > - <TrashIcon className="w-5 h-5" /> - </div> - </div> - </div> - {isVisible && ( - <div className="lg:hidden w-full flex justify-center z-40"> - <div className="grid grid-cols-2 grid-rows-2 place-items-center w-full px-5 z-30 gap-4"> - {/* GENRES */} - <MultiSelector - data={genreOptions} - other={tagsOption} - selected={genre} - setSelected={setGenre} - label="Genres" - inputRef={inputRef} - /> - {/* SORT */} - {/* <SingleSelector - data={sortOptions} - selected={sort} - setSelected={setSelectedSort} - label="Sort" - /> */} - {/* FORMAT */} - <SingleSelector - data={index === 0 ? animeFormatOptions : mangaFormatOptions} - selected={format} - setSelected={setFormat} - label="Format" - /> - {/* SEASON */} - <SingleSelector - data={seasonOptions} - selected={season} - setSelected={setSeason} - label="Season" - /> - {/* YEAR */} - <SingleSelector - data={yearOptions} - selected={year} - setSelected={setYear} - label="Year" - /> - </div> - </div> - )} - {/* <div> */} - <div className="flex flex-col gap-14 items-center z-30"> - <div - key="card-keys" - className={`${ - imageSearch ? "hidden" : "" - } grid pt-3 px-5 xl:px-0 xxs:grid-cols-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 xl:grid-cols-6 justify-items-center grid-cols-2 w-screen xl:w-auto xl:gap-7 gap-5 gap-y-10`} - > - {loading - ? "" - : !data && ( - <div className="w-full text-[#ff7f57] col-span-6 items-center flex justify-center text-center font-bold font-karla xl:text-2xl"> - Oops!<br></br> Nothing's Found... - </div> - )} - - {data && - data?.length > 0 && - !imageSearch && - data?.map((anime, index) => { - const anilistId = anime?.mappings?.find( - (x) => x.providerId === "anilist" - )?.id; - return ( - <m.div - initial={{ scale: 0.98 }} - animate={{ scale: 1, transition: { duration: 0.35 } }} - className="w-full" - key={index} - > - <Link - href={ - anime.format === "MANGA" || anime.format === "NOVEL" - ? `/en/manga/${anilistId ? `${anilistId}/` : ""}${ - anime.id - }` - : `/en/anime/${anime.id}` - } - title={anime.title.userPreferred} - className="block relative overflow-hidden bg-secondary hover:scale-[1.03] scale-100 transition-all cursor-pointer duration-200 ease-out rounded" - style={{ - paddingTop: "145%", // 2:3 aspect ratio (3/2 * 100%) - }} - > - <Image - className="object-cover" - src={anime.coverImage.extraLarge} - alt={anime.title.userPreferred} - sizes="(min-width: 808px) 50vw, 100vw" - quality={100} - fill - /> - </Link> - <Link - href={ - anime.format === "MANGA" || anime.format === "NOVEL" - ? `/en/manga/${anilistId ? `${anilistId}/` : ""}${ - anime.id - }` - : `/en/anime/${anime.id}` - } - title={anime.title.userPreferred} - > - <h1 className="font-outfit font-bold xl:text-base text-[15px] pt-4 line-clamp-2"> - {anime.status === "RELEASING" ? ( - <span className="dots bg-green-500" /> - ) : anime.status === "NOT_YET_RELEASED" ? ( - <span className="dots bg-red-500" /> - ) : null} - {anime.title.userPreferred} - </h1> - </Link> - <h2 className="font-outfit xl:text-[15px] text-[11px] font-light pt-2 text-[#8B8B8B]"> - {anime.format || <p>-</p>} ·{" "} - {anime.status || <p>-</p>} ·{" "} - {anime.episodes - ? `${anime.episodes || "N/A"} Episodes` - : `${anime.chapters || "N/A"} Chapters`} - </h2> - </m.div> - ); - })} - - {loading && ( - <> - {[1, 2, 4, 5, 6, 7, 8].map((item) => ( - <div className="w-full" key={item}> - <div className="w-full"> - <Skeleton - className="w-full rounded" - style={{ - paddingTop: "140%", // 2:3 aspect ratio (3/2 * 100%) - width: "(min-width: 808px) 50vw, 100vw", - lineHeight: 1, - }} - /> - </div> - <div> - <h1 className="font-outfit w-[320px] font-bold xl:text-base text-[15px] pt-4 line-clamp-2"> - <Skeleton width={120} height={26} /> - </h1> - </div> - </div> - ))} - </> - )} - </div> - - {imageSearch && ( - <div className="grid grid-cols-1 xs:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-4 gap-3 md:gap-7 px-5 lg:px-0"> - {imageSearch.map((a, index) => { - return ( - <m.div - key={index} - initial={{ scale: 0.9 }} - animate={{ scale: 1, transition: { duration: 0.35 } }} - className="flex flex-col gap-2 shrink-0 cursor-pointer relative group/item" - > - <Link - className="relative aspect-video rounded-md overflow-hidden group" - href={`/en/anime/${a.anilist.id}`} - onMouseEnter={() => { - handleVideoHover(true, a.filename); - }} - onMouseLeave={() => handleVideoHover(false, a.filename)} - > - <div className="w-full h-full bg-gradient-to-t from-black/70 from-20% to-transparent group-hover:to-black/40 transition-all duration-300 ease-out absolute z-30" /> - <div className="absolute bottom-3 left-0 mx-2 text-white flex gap-2 items-center w-[80%] z-30"> - <PlayIcon className="w-5 h-5 shrink-0" /> - <h1 - className="font-semibold font-karla line-clamp-1" - title={a?.anilist.title.romaji} - > - {`Episode ${a.episode}`} - </h1> - </div> - - {a?.image && ( - <Image - src={a?.image} - width={200} - height={200} - alt="Episode Thumbnail" - className={`w-full object-cover group-hover:scale-[1.02] duration-300 ease-out z-10 ${ - !a.hovered ? "visible" : "hidden" - }`} - /> - )} - {a?.video && ( - <video - src={a.video} - className={`w-full object-cover group-hover:scale-[1.02] duration-300 ease-out z-10 ${ - a.hovered ? "visible" : "hidden" - }`} - autoPlay - muted - loop - playsInline - /> - )} - </Link> - - <Link - className="flex flex-col font-karla w-full" - href={`/en/anime/${a.anilist.id}`} - > - {/* <h1 className="font-semibold">{a.title}</h1> */} - <p className="flex items-center gap-1 text-sm text-gray-400 w-[320px]"> - <span - className="text-white max-w-[120px] md:max-w-[200px] lg:max-w-[220px]" - style={{ - display: "inline-block", - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap", - }} - title={a?.anilist.title.romaji} - > - {a?.anilist.title.romaji} - </span>{" "} - | Episode {a.episode} - </p> - </Link> - </m.div> - ); - })} - </div> - )} - {!loading && page > 10 && nextPage && ( - <button - onClick={() => setPage((p) => p + 1)} - className="bg-secondary xl:w-[30%] w-[80%] h-10 rounded-md" - > - Load More - </button> - )} - </div> - {/* </div> */} - </div> - </main> - <Footer /> - </> - ); -} |